//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//    NotificationRulesViewerTest.cs
//
// Abstract:
//    
//    Unit tests for the notification rules UI.
//
// Classes:
//
//    Microsoft.EnhancedReminders.OutlookAddIn.UnitTests.NotificationRulesViewerTest
//
//--

using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.OfficeCommunicationsServer.Applications.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.EnhancedReminders.OutlookAddIn.UnitTests
{   
    /// <summary>
    ///This is a test class for NotificationRulesViewerTest and is intended
    ///to contain all NotificationRulesViewerTest Unit Tests
    ///</summary>
    [TestClass()]
    public class NotificationRulesViewerTest
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        /// <summary>
        /// Helper class to create the NotificationRulesViewer control and auxiliary components.
        /// </summary>
        internal class NotificationRulesViewerHelper
        {
            private DummyRulesLoader _rulesLoader;
            private NotificationRulesViewer _rulesViewer;
            private NotificationRulesViewer_Accessor _accessor;

            /// <summary>
            /// Constructor.
            /// </summary>
            public NotificationRulesViewerHelper()
                : this(false)
            {
            }

            /// <summary>
            /// Constructor.
            /// </summary>
            /// <param name="populateRules">True to populate the rules viewer with rules for testing.</param>
            public NotificationRulesViewerHelper(bool populateRules)
            {
                _rulesLoader = new DummyRulesLoader(populateRules);

                // Suppress the delete warning dialog for unit testing.
                _rulesLoader.StoredRules.DoNotPromptUserOnDelete = true;

                // Create an accessor to provide access to private methods on the rules viewer.
                _accessor = new NotificationRulesViewer_Accessor(_rulesLoader);

                // As the rules viewer is not being instantiated for display, we need
                // to fire the Loaded event that WPF calls once the conrol is displayed.
                _accessor.OnRulesViewerLoaded(this, new RoutedEventArgs());

                _rulesViewer = Helpers.AssertCast<NotificationRulesViewer>(_accessor.Target);

                // Post-condition - if rules viewer has been populated it should contain 
                Assert.IsNotNull(_accessor.Rules, "Rules list should have been created");
                Assert.IsTrue((!populateRules && _accessor.Rules.Count == 1) || // For the "empty list" message.
                              (populateRules && _accessor.Rules.Count == _rulesLoader.StoredRules.Count));
            }

            public DummyRulesLoader RulesLoader
            {
                get
                {
                    return _rulesLoader;
                }
            }

            public NotificationRulesViewer RulesViewer
            {
                get
                {
                    return _rulesViewer;
                }
            }

            public NotificationRulesViewer_Accessor Accessor
            {
                get
                {
                    return _accessor;
                }
            }
        }

        /// <summary>
        /// Verify rules are displayed in the order they are loaded.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("Microsoft.EnhancedReminders.OutlookAddIn.dll")]
        public void TestRulesListIsCorrectlyPopulated()
        {
            NotificationRulesViewerHelper helper = new NotificationRulesViewerHelper(true);

            ListView listView = GetItemHelper<ListView>(helper.RulesViewer, "rulesListView");

            CompareRulesHelper(helper.RulesLoader.StoredRules, listView.ItemsSource as NotificationRules);

            VerifyState(helper.RulesViewer,
                        0,     // selectedIndex
                        true,  // add button
                        true,  // edit button
                        true,  // delete button
                        false, // up button 
                        true,  // down button
                        true,  // enable all rules checkbox
                        true,  // ok button
                        true,  // cancel button
                        true   // apply button
                        );
        }

        /// <summary>
        /// Verify an empty list of rules is loaded and displays the "list is empty" message.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("Microsoft.EnhancedReminders.OutlookAddIn.dll")]
        public void TestRulesListLoadsEmptyList()
        {
            NotificationRulesViewerHelper helper = new NotificationRulesViewerHelper();

            ListView listView = GetItemHelper<ListView>(helper.RulesViewer, "rulesListView");

            // Create the dummy "rule" which should appear when the list is empty.
            NotificationRules expectedRules = new NotificationRules();
            expectedRules.Add(new NotificationRuleItem(MailboxType.Voicemail, Properties.Resources.RulesDialogEmptyList, DateTime.Now, Microsoft.EnhancedReminders.UnitTests.Helpers.CreateDummyChannels()));

            CompareRulesHelper(expectedRules, listView.ItemsSource as NotificationRules);

            VerifyState(helper.RulesViewer,
                        -1,    // selectedIndex
                        true,  // add button
                        false, // edit button
                        false, // delete button
                        false, // up button 
                        false, // down button
                        false,  // enable all rules checkbox
                        true,  // ok button
                        true,  // cancel button
                        true   // apply button
                        );
        }

        /// <summary>
        /// Verify deleting a rule.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("Microsoft.EnhancedReminders.OutlookAddIn.dll")]
        public void TestRuleCanBeDeleted()
        {
            NotificationRulesViewerHelper helper = new NotificationRulesViewerHelper(true);

            ListView listView = GetItemHelper<ListView>(helper.RulesViewer, "rulesListView");

            // Verify that the UI is enabled for deletion.
            Button button = GetItemHelper<Button>(helper.RulesViewer, "deleteButton");
            Assert.IsTrue(NotificationRulesViewer.DeleteCommand.CanExecute(helper.RulesViewer, button), "Delete button should be enabled");

            // Delete the second rule.
            listView.SelectedIndex = 1;
            NotificationRulesViewer.DeleteCommand.Execute(helper.RulesViewer, button);

            // Remove second rule from the stored rules and compare
            helper.RulesLoader.StoredRules.RemoveAt(1);
            CompareRulesHelper(helper.RulesLoader.StoredRules, listView.ItemsSource as NotificationRules);

            VerifyState(helper.RulesViewer,
                        0,     // selectedIndex
                        true,  // add button
                        true,  // edit button
                        true,  // delete button
                        false, // up button 
                        true,  // down button
                        true,  // enable all rules checkbox
                        true,  // ok button
                        true,  // cancel button
                        true   // apply button
                        );
        }

        /// <summary>
        /// Verify deleting all rules results in the "empty list" message.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("Microsoft.EnhancedReminders.OutlookAddIn.dll")]
        public void TestAllRulesCanBeDeleted()
        {
            NotificationRulesViewerHelper helper = new NotificationRulesViewerHelper(true);

            ListView listView = GetItemHelper<ListView>(helper.RulesViewer, "rulesListView");

            // Verify that the UI is enabled for deletion.
            Button button = GetItemHelper<Button>(helper.RulesViewer, "deleteButton");
            Assert.IsTrue(NotificationRulesViewer.DeleteCommand.CanExecute(helper.RulesViewer, button), "Delete button should be enabled");

            // Select the last (third) rule and delete all rules.
            listView.SelectedIndex = 2;
            NotificationRulesViewer.DeleteCommand.Execute(helper.RulesViewer, button);
            NotificationRulesViewer.DeleteCommand.Execute(helper.RulesViewer, button);
            NotificationRulesViewer.DeleteCommand.Execute(helper.RulesViewer, button);

            // Create the dummy "rule" which should appear when the list is empty.
            NotificationRules expectedRules = new NotificationRules();
            expectedRules.Add(new NotificationRuleItem(MailboxType.Voicemail, Properties.Resources.RulesDialogEmptyList, DateTime.Now, Microsoft.EnhancedReminders.UnitTests.Helpers.CreateDummyChannels()));

            CompareRulesHelper(expectedRules, listView.ItemsSource as NotificationRules);

            VerifyState(helper.RulesViewer,
                        -1,    // selectedIndex
                        true,  // add button
                        false, // edit button
                        false, // delete button
                        false, // up button 
                        false, // down button
                        false,  // enable all rules checkbox
                        true,  // ok button
                        true,  // cancel button
                        true   // apply button
                        );
        }

        /// <summary>
        /// Verify moving a rule up from the bottom to the top and the "Up" button becomes
        /// grayed out.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("Microsoft.EnhancedReminders.OutlookAddIn.dll")]
        public void TestMovingRuleUp()
        {
            NotificationRulesViewerHelper helper = new NotificationRulesViewerHelper(true);

            ListView listView = GetItemHelper<ListView>(helper.RulesViewer, "rulesListView");

            // Select the last (third) rule and move it up to the top of the list.
            listView.SelectedIndex = 2;

            // Verify that the UI is enabled for moving up.
            Button button = GetItemHelper<Button>(helper.RulesViewer, "upButton");
            Assert.IsTrue(NotificationRulesViewer.UpCommand.CanExecute(helper.RulesViewer, button), "Up button should be enabled");

            // Move it up twice
            NotificationRulesViewer.UpCommand.Execute(helper.RulesViewer, button);
            NotificationRulesViewer.UpCommand.Execute(helper.RulesViewer, button);

            // Create a list in the expected order after moving up.
            NotificationRules expectedOrder = new NotificationRules();
            expectedOrder.Add(helper.RulesLoader.StoredRules[2]);
            expectedOrder.Add(helper.RulesLoader.StoredRules[0]);
            expectedOrder.Add(helper.RulesLoader.StoredRules[1]);

            CompareRulesHelper(expectedOrder, listView.ItemsSource as NotificationRules);

            VerifyState(helper.RulesViewer,
                        0,    // selectedIndex
                        true,  // add button
                        true, // edit button
                        true, // delete button
                        false, // up button 
                        true, // down button
                        true,  // enable all rules checkbox
                        true,  // ok button
                        true,  // cancel button
                        true   // apply button
                        );
        }

        /// <summary>
        /// Verify moving a rule down from the top to the bottom and the "Down" button becomes
        /// grayed out.
        ///</summary>
        [TestMethod()]
        [DeploymentItem("Microsoft.EnhancedReminders.OutlookAddIn.dll")]
        public void TestMovingRuleDown()
        {
            NotificationRulesViewerHelper helper = new NotificationRulesViewerHelper(true);

            ListView listView = GetItemHelper<ListView>(helper.RulesViewer, "rulesListView");

            // Select the first rule and move it down to the bottom of the list.
            listView.SelectedIndex = 0;

            // Verify that the UI is enabled for moving down.
            Button button = GetItemHelper<Button>(helper.RulesViewer, "downButton");
            Assert.IsTrue(NotificationRulesViewer.DownCommand.CanExecute(helper.RulesViewer, button), "Down button should be enabled");

            // Move the  up twice
            NotificationRulesViewer.DownCommand.Execute(helper.RulesViewer, button);
            NotificationRulesViewer.DownCommand.Execute(helper.RulesViewer, button);

            // Create a list with the expected post-move order.
            NotificationRules expectedOrder = new NotificationRules();
            expectedOrder.Add(helper.RulesLoader.StoredRules[1]);
            expectedOrder.Add(helper.RulesLoader.StoredRules[2]);
            expectedOrder.Add(helper.RulesLoader.StoredRules[0]);

            CompareRulesHelper(expectedOrder, listView.ItemsSource as NotificationRules);

            VerifyState(helper.RulesViewer,
                        2,    // selectedIndex
                        true,  // add button
                        true, // edit button
                        true, // delete button
                        true, // up button 
                        false, // down button
                        true,  // enable all rules checkbox
                        true,  // ok button
                        true,  // cancel button
                        true   // apply button
                        );
        }

        /// <summary>
        /// Helper method to compare the displayed rules against a target list.
        /// </summary>
        /// <param name="targetRules">Target rules to compare against.</param>
        /// <param name="displayedRules">The displayed rules.</param>
        private void CompareRulesHelper(NotificationRules targetRules, NotificationRules displayedRules)
        {
            Assert.IsNotNull(targetRules, "targetRules");
            Assert.IsNotNull(displayedRules, "displayedRules");

            Assert.AreEqual(targetRules.Count, displayedRules.Count, "Unexpected rule count");

            for (int i = 0; i < targetRules.Count; i++)
            {
                NotificationRuleItem targetRule = targetRules[i];
                NotificationRuleItem displayedRule = displayedRules[i];

                Assert.AreEqual(targetRule.RuleName, displayedRule.RuleName, "Unexpected rule name");
                Assert.AreEqual(targetRule.IsEnabled, displayedRule.IsEnabled, "Unexpected IsEnabled setting");
                Assert.AreEqual(targetRule.MailboxType, displayedRule.MailboxType, "Unexpected MailboxType");
            }
        }

        /// <summary>
        /// Verify the various UI controls are in the expected state.
        /// </summary>
        private void VerifyState(NotificationRulesViewer rulesViewer, 
                                 int selectedIndex, 
                                 bool addButton, 
                                 bool editButton, 
                                 bool deleteButton,
                                 bool upButton, 
                                 bool downButton,
                                 bool enableAllRules,
                                 bool okButton, 
                                 bool cancelButton, 
                                 bool applyButton)
        {
            Assert.IsNotNull(rulesViewer, "rulesViewer");

            ListView listView = GetItemHelper<ListView>(rulesViewer, "rulesListView");

            Assert.AreEqual(selectedIndex, listView.SelectedIndex, "Unexpected rule selected");

            Button button = GetItemHelper<Button>(rulesViewer, "addButton");
            bool isEnabled = NotificationRulesViewer.AddCommand.CanExecute(rulesViewer, button);
            Assert.AreEqual(addButton, isEnabled, "Unexpected state for add button");

            button = GetItemHelper<Button>(rulesViewer, "editButton");
            isEnabled = NotificationRulesViewer.EditCommand.CanExecute(rulesViewer, button);
            Assert.AreEqual(editButton, isEnabled, "Unexpected state for edit button");

            button = GetItemHelper<Button>(rulesViewer, "deleteButton");
            isEnabled = NotificationRulesViewer.DeleteCommand.CanExecute(rulesViewer, button);
            Assert.AreEqual(deleteButton, isEnabled, "Unexpected state for delete button");

            button = GetItemHelper<Button>(rulesViewer, "upButton");
            isEnabled = NotificationRulesViewer.UpCommand.CanExecute(rulesViewer, button);
            Assert.AreEqual(upButton, isEnabled, "Unexpected state for up button");

            button = GetItemHelper<Button>(rulesViewer, "downButton");
            isEnabled = NotificationRulesViewer.DownCommand.CanExecute(rulesViewer, button);
            Assert.AreEqual(downButton, isEnabled, "Unexpected state for down button");
            
            /* TODO: Figure out why CanExecute() is never invoked for the checkbox.
            CheckBox checkbox = GetEnableAllCheckbox(rulesViewer);
            isEnabled = NotificationRulesViewer.EnableAllRulesCommand.CanExecute(rulesViewer, checkbox);
            Assert.AreEqual(enableAllRules, isEnabled, "Unexpected state for enable all checkbox");
            */

            button = GetItemHelper<Button>(rulesViewer, "okButton");
            isEnabled = NotificationRulesViewer.CancelCommand.CanExecute(rulesViewer, button);
            Assert.AreEqual(okButton, isEnabled, "Unexpected state for ok button");

            button = GetItemHelper<Button>(rulesViewer, "cancelButton");
            isEnabled = NotificationRulesViewer.CancelCommand.CanExecute(rulesViewer, button);
            Assert.AreEqual(cancelButton, isEnabled, "Unexpected state for cancel button");

            button = GetItemHelper<Button>(rulesViewer, "applyButton");
            isEnabled = NotificationRulesViewer.ApplyCommand.CanExecute(rulesViewer, button);
            Assert.AreEqual(applyButton, isEnabled, "Unexpected state for apply button");
        }

        /// <summary>
        /// Helper method to obtain an item from the rules view user control.
        /// </summary>
        /// <typeparam name="T">Type of item to obtain.</typeparam>
        /// <param name="rulesViewer">The rules viewer.</param>
        /// <param name="name">Name of the target item to obain.</param>
        /// <returns>The target item.</returns>
        private T GetItemHelper<T>(NotificationRulesViewer rulesViewer, string name)
        {
            Assert.IsNotNull(rulesViewer, "rulesViewer");
            Assert.IsFalse(string.IsNullOrEmpty(name), "name");

            T item = (T)rulesViewer.FindName(name);

            Assert.IsNotNull(item, "Could not find item of type " + typeof(T).ToString() + " with name " + name);

            return item;
        }
        
        /// <summary>
        /// Helper method to obtain the "enable all" checkbox.
        /// </summary>
        /// <param name="rulesViewer">The rules viewer user control.</param>
        /// <returns>The "enable all" checkbox.</returns>
        private CheckBox GetEnableAllCheckbox(NotificationRulesViewer rulesViewer)
        {
            Assert.IsNotNull(rulesViewer, "rulesViewer");
       
            DataTemplate template = rulesViewer.Resources["EnableAllRules"] as DataTemplate;
            Assert.IsNotNull(template, "template");

            StackPanel panel = template.LoadContent() as StackPanel;
            Assert.IsNotNull(panel, "panel");

            Assert.IsTrue(panel.Children.Count == 1, "Expect one element in panel");

            CheckBox enableAllCheckbox = Helpers.AssertCast<CheckBox>(panel.Children[0]);

            return enableAllCheckbox;
        }
    }
}
